﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine;
using Engine.MapSystem;
using Engine.MathEx;
using Engine.Renderer;
using Engine.EntitySystem;
using Engine.FileSystem;
using Engine.UISystem;
using Engine.SoundSystem;
using Engine.PhysicsSystem;
using Engine.Utils;
using GameCommon;
using GameEntities;
using Game;
using System.IO;

namespace Arcanea.MMORPGClient
{
    class ArcaneaEngineApp : EngineApp
    {

        #region Etat du type de jeux actuel

        private static GameMap.GameTypes actualGameType;
        private static bool changed = false;
        public static GameMap.GameTypes ActualGameType
        {
            get { return actualGameType; }
            set
            {
                changed = true;
                actualGameType = value;
            }
        }

        #endregion

        #region Parameters

        protected static float gamma = 1;
        [Config("Video", "gamma")]
        public static float _Gamma
        {
            get { return gamma; }
            set
            {
                gamma = value;
                EngineApp.Instance.Gamma = gamma;
            }
        }

        protected static bool showSystemCursor;
        [Config("Video", "showSystemCursor")]
        public static bool _ShowSystemCursor
        {
            get { return showSystemCursor; }
            set
            {
                showSystemCursor = value;

                EngineApp.Instance.ShowSystemCursor = value;

                if (EngineApp.Instance.ShowSystemCursor)
                {
                    if (ScreenControlManager.Instance != null)
                        ScreenControlManager.Instance.DefaultCursor = null;
                }
                else
                {
                    string cursorName = "Cursors\\Default.png";
                    if (!VirtualFile.Exists(cursorName))
                        cursorName = null;
                    if (ScreenControlManager.Instance != null)
                        ScreenControlManager.Instance.DefaultCursor = cursorName;
                    if (cursorName == null)
                        EngineApp.Instance.ShowSystemCursor = true;
                }
            }
        }

        protected static bool drawFPS;
        [Config("Video", "drawFPS")]
        public static bool _DrawFPS
        {
            get { return drawFPS; }
            set
            {
                drawFPS = value;
                EngineApp.Instance.ShowFPS = value;
            }
        }

        protected static MaterialSchemes materialScheme = MaterialSchemes.Default;
        [Config("Video", "materialScheme")]
        public static MaterialSchemes MaterialScheme
        {
            get { return materialScheme; }
            set
            {
                materialScheme = value;
                if (RendererWorld.Instance != null)
                    RendererWorld.Instance.DefaultViewport.MaterialScheme = materialScheme.ToString();
            }
        }

        protected static ShadowTechniques shadowTechnique = ShadowTechniques.ShadowmapHigh;
        [Config("Video", "shadowTechnique")]
        public static ShadowTechniques ShadowTechnique
        {
            get { return shadowTechnique; }
            set { shadowTechnique = value; }
        }

        //this options affect to shadowColor and shadowFarDistance
        protected static bool shadowUseMapSettings = true;
        [Config("Video", "shadowUseMapSettings")]
        public static bool ShadowUseMapSettings
        {
            get { return shadowUseMapSettings; }
            set { shadowUseMapSettings = value; }
        }

        protected static ColorValue shadowColor = new ColorValue(.75f, .75f, .75f);
        [Config("Video", "shadowColor")]
        public static ColorValue ShadowColor
        {
            get { return shadowColor; }
            set { shadowColor = value; }
        }

        protected static float shadowFarDistance = 30.0f;
        [Config("Video", "shadowFarDistance")]
        public static float ShadowFarDistance
        {
            get { return shadowFarDistance; }
            set { shadowFarDistance = value; }
        }

        protected static int shadow2DTextureSize = 1024;
        [Config("Video", "shadow2DTextureSize")]
        public static int Shadow2DTextureSize
        {
            get { return shadow2DTextureSize; }
            set { shadow2DTextureSize = value; }
        }

        protected static int shadow2DTextureCount = 2;
        [Config("Video", "shadow2DTextureCount")]
        public static int Shadow2DTextureCount
        {
            get { return shadow2DTextureCount; }
            set { shadow2DTextureCount = value; }
        }

        protected static int shadowCubicTextureSize = 512;
        [Config("Video", "shadowCubicTextureSize")]
        public static int ShadowCubicTextureSize
        {
            get { return shadowCubicTextureSize; }
            set { shadowCubicTextureSize = value; }
        }

        protected static int shadowCubicTextureCount = 1;
        [Config("Video", "shadowCubicTextureCount")]
        public static int ShadowCubicTextureCount
        {
            get { return shadowCubicTextureCount; }
            set { shadowCubicTextureCount = value; }
        }

        protected static WaterPlane.ReflectionLevels waterReflectionLevel = WaterPlane.ReflectionLevels.ReflectAll;
        [Config("Video", "waterReflectionLevel")]
        public static WaterPlane.ReflectionLevels WaterReflectionLevel
        {
            get { return waterReflectionLevel; }
            set { waterReflectionLevel = value; }
        }

        protected static float soundVolume = .8f;
        [Config("Sound", "soundVolume")]
        public static float SoundVolume
        {
            get { return soundVolume; }
            set
            {
                soundVolume = value;
                if (EngineApp.Instance.DefaultSoundChannelGroup != null)
                    EngineApp.Instance.DefaultSoundChannelGroup.Volume = soundVolume;
            }
        }

        protected static float musicVolume = .8f;
        [Config("Sound", "musicVolume")]
        public static float MusicVolume
        {
            get { return musicVolume; }
            set
            {
                musicVolume = value;
                if (GameMusic.MusicChannelGroup != null)
                    GameMusic.MusicChannelGroup.Volume = musicVolume;
            }
        }

        [Config("Environment", "autorunMapName")]
        public static string autorunMapName = "";

        #endregion

        #region Instance pattern 

        private static ArcaneaEngineApp instance;
        public static new ArcaneaEngineApp Instance
        {
            get { return instance; }
        }

        #endregion

        protected void ChangeToBetterDefaultSettings()
        {
            bool shadowTechniqueInitialized = false;
            bool shadowTextureSizeInitialized = false;

            string error;
            TextBlock block = TextBlockUtils.LoadFromRealFile(EngineApp.ConfigName, out error);
            if (block != null)
            {
                TextBlock blockVideo = block.FindChild("Video");
                if (blockVideo != null)
                {
                    if (blockVideo.IsAttributeExist("shadowTechnique"))
                        shadowTechniqueInitialized = true;
                    if (blockVideo.IsAttributeExist("shadow2DTextureSize"))
                        shadowTextureSizeInitialized = true;
                }
            }

            //shadowTechnique
            if (!shadowTechniqueInitialized)
            {
                if (RenderSystem.Instance.GPUIsGeForce())
                {
                    if (RenderSystem.Instance.GPUCodeName >= GPUCodeNames.GeForce_NV10 &&
                        RenderSystem.Instance.GPUCodeName <= GPUCodeNames.GeForce_NV40)
                    {
                        shadowTechnique = ShadowTechniques.ShadowmapLow;
                    }
                    if (RenderSystem.Instance.GPUCodeName == GPUCodeNames.GeForce_G70)
                        shadowTechnique = ShadowTechniques.ShadowmapMedium;
                }

                if (RenderSystem.Instance.GPUIsRadeon())
                {
                    if (RenderSystem.Instance.GPUCodeName >= GPUCodeNames.Radeon_R100 &&
                        RenderSystem.Instance.GPUCodeName <= GPUCodeNames.Radeon_R400)
                    {
                        shadowTechnique = ShadowTechniques.ShadowmapLow;
                    }
                    if (RenderSystem.Instance.GPUCodeName == GPUCodeNames.Radeon_R500)
                        shadowTechnique = ShadowTechniques.ShadowmapMedium;
                }

                if (!RenderSystem.Instance.HasShaderModel2())
                    shadowTechnique = ShadowTechniques.None;
            }

            //shadow texture size
            if (!shadowTextureSizeInitialized)
            {
                if (RenderSystem.Instance.GPUIsGeForce() &&
                    RenderSystem.Instance.GPUCodeName >= GPUCodeNames.GeForce_G80)
                {
                    shadow2DTextureSize = 2048;
                }
                if (RenderSystem.Instance.GPUIsRadeon() &&
                    RenderSystem.Instance.GPUCodeName >= GPUCodeNames.Radeon_R600)
                {
                    shadow2DTextureSize = 2048;
                }
            }
        }

        protected override bool OnCreate()
        {
            instance = this;

            ChangeToBetterDefaultSettings();

            if (!base.OnCreate())
                return false;

            SoundVolume = soundVolume;
            MusicVolume = musicVolume;

            ScreenControlManager.Init();
            if (!ControlsWorld.Init())
                return false;

            _ShowSystemCursor = _ShowSystemCursor;
            _DrawFPS = _DrawFPS;
            MaterialScheme = materialScheme;

            EControl programLoadingWindow = ControlDeclarationManager.Instance.CreateControl(
                "Gui\\ProgramLoadingWindow.gui");
            if (programLoadingWindow != null)
                ScreenControlManager.Instance.Controls.Add(programLoadingWindow);

            RenderScene();

            EngineConsole.Instance.Texture = TextureManager.Instance.Load("Utils/Console.dds",
                Texture.Type.Type2D, 0);
            EngineConsole.Instance.Font = FontManager.Instance.LoadFont("Default", .025f);
            EngineConsole.Instance.StaticText = "Version " + EngineVersionInformation.Version;

            #region Logs ...

            Log.Handlers.InfoHandler += delegate(string text)
            {
                if (EngineConsole.Instance != null)
                    EngineConsole.Instance.Print(text);
            };

            Log.Handlers.WarningHandler += delegate(string text, ref bool handled)
            {
                if (EngineConsole.Instance != null)
                {
                    handled = true;
                    EngineConsole.Instance.Print("Warning: " + text, new ColorValue(1, 0, 0));
                    EngineConsole.Instance.Active = true;
                }
            };

            Log.Handlers.ErrorHandler += delegate(string text, ref bool handled)
            {
                if (ScreenControlManager.Instance != null)
                {
                    handled = true;

                    //find already created MessageBoxWindow
                    foreach (EControl control in ScreenControlManager.Instance.Controls)
                    {
                        if (control is MessageBoxWindow)
                            return;
                    }

                    if (Map.Instance != null)
                        EntitySystemWorld.Instance.Simulation = false;

                    EngineApp.Instance.MouseRelativeMode = false;

                    ScreenControlManager.Instance.Controls.Add(new MessageBoxWindow(text, "Error",
                        delegate(EButton sender)
                        {
                            ScreenControlManager.Instance.Controls.Clear();

                            if (EntitySystemWorld.Instance == null)
                            {
                                EngineApp.Instance.ShouldExit();
                                return;
                            }

                            EntitySystemWorld.Instance.Simulation = false;

                            ScreenControlManager.Instance.Controls.Add(new MainMenuWindow());

                        }));
                }
            };

            Log.Handlers.FatalHandler += delegate(string text, ref bool handled)
            {
                if (ScreenControlManager.Instance != null)
                {
                    //find already created MessageBoxWindow
                    foreach (EControl control in ScreenControlManager.Instance.Controls)
                    {
                        if (control is MessageBoxWindow)
                        {
                            handled = true;
                            return;
                        }
                    }
                }
            };

            #endregion

            //MotionBlur for a player unit contusion
            {
                Compositor compositor = CompositorManager.Instance.GetByName("MotionBlur");
                if (compositor != null && compositor.IsSupported())
                    RendererWorld.Instance.DefaultViewport.AddCompositor("MotionBlur");
            }

            if (programLoadingWindow != null)
                programLoadingWindow.SetShouldDetach();

            //Game controls
            GameControlsManager.Init();

            //EntitySystem
            if (!EntitySystemWorld.Init(new EntitySystemWorld()))
                return true;// false;

            //Change le type actuel de jeu.
            ActualGameType = GameMap.GameTypes.MMORPGPlayerLogin;

            return true;
        }

        protected override void OnDestroy()
        {
            MapSystemWorld.MapDestroy();
            EntitySystemWorld.Shutdown();

            GameControlsManager.Shutdown();

            ControlsWorld.Shutdown();
            ScreenControlManager.Shutdown();

            EngineConsole.Shutdown();

            instance = null;
            base.OnDestroy();
        }

        protected override bool OnKeyDown(KeyEvent e)
        {
            if (EngineConsole.Instance.OnKeyDown(e))
                return true;

            if (ScreenControlManager.Instance != null)
                if (ScreenControlManager.Instance.DoKeyDown(e))
                    return true;

            //Debug information window
            if (e.Key == EKeys.F11)
            {
                if (DebugInformationWindow.Instance == null)
                {
                    DebugInformationWindow window = new DebugInformationWindow();
                    ScreenControlManager.Instance.Controls.Add(window);
                }
                else
                {
                    DebugInformationWindow.Instance.SetShouldDetach();
                }
                return true;
            }

            //make screenshot
            if (e.Key == EKeys.F12)
            {
                if (!Directory.Exists("Screenshots"))
                    Directory.CreateDirectory("Screenshots");

                string format = "Screenshots\\Screenshot{0}.tga";

                for (int n = 1; n < 1000; n++)
                {
                    string v = n.ToString();
                    if (n < 10)
                        v = "0" + v;
                    if (n < 100)
                        v = "0" + v;

                    string fileName = string.Format(format, v);

                    if (!File.Exists(fileName))
                    {
                        RendererWorld.Instance.RenderWindow.WriteContentsToFile(fileName);
                        break;
                    }
                }

                return true;
            }

            return base.OnKeyDown(e);
        }

        protected override bool OnKeyPress(KeyPressEvent e)
        {
            if (EngineConsole.Instance.OnKeyPress(e))
                return true;
            if (ScreenControlManager.Instance != null)
                if (ScreenControlManager.Instance.DoKeyPress(e))
                    return true;
            return base.OnKeyPress(e);
        }

        protected override bool OnKeyUp(KeyEvent e)
        {
            if (ScreenControlManager.Instance != null)
                if (ScreenControlManager.Instance.DoKeyUp(e))
                    return true;
            return base.OnKeyUp(e);
        }

        protected override bool OnMouseDown(EMouseButtons button)
        {
            if (ScreenControlManager.Instance != null)
                if (ScreenControlManager.Instance.DoMouseDown(button))
                    return true;
            return base.OnMouseDown(button);
        }

        protected override bool OnMouseUp(EMouseButtons button)
        {
            if (ScreenControlManager.Instance != null)
                if (ScreenControlManager.Instance.DoMouseUp(button))
                    return true;
            return base.OnMouseUp(button);
        }

        protected override bool OnMouseDoubleClick(EMouseButtons button)
        {
            if (ScreenControlManager.Instance != null)
                if (ScreenControlManager.Instance.DoMouseDoubleClick(button))
                    return true;
            return base.OnMouseDoubleClick(button);
        }

        protected override void OnMouseMove(Vec2 mouse)
        {
            base.OnMouseMove(mouse);
            if (ScreenControlManager.Instance != null)
                ScreenControlManager.Instance.DoMouseMove(mouse);
        }

        protected override bool OnMouseWheel(int delta)
        {
            if (ScreenControlManager.Instance != null)
                if (ScreenControlManager.Instance.DoMouseWheel(delta))
                    return true;
            return base.OnMouseWheel(delta);
        }

        protected override bool OnJoystickEvent(JoystickInputEvent e)
        {
            if (ScreenControlManager.Instance != null)
                if (ScreenControlManager.Instance.DoJoystickEvent(e))
                    return true;
            return base.OnJoystickEvent(e);
        }

        protected override bool OnCustomInputDeviceEvent(InputEvent e)
        {
            if (ScreenControlManager.Instance != null)
                if (ScreenControlManager.Instance.DoCustomInputDeviceEvent(e))
                    return true;
            return base.OnCustomInputDeviceEvent(e);
        }

        protected override void OnSystemPause(bool pause)
        {
            base.OnSystemPause(pause);

            if (EntitySystemWorld.Instance != null)
                EntitySystemWorld.Instance.SystemPauseOfSimulation = pause;
        }

        protected override void OnTick(float delta)
        {
            base.OnTick(delta);

            if (changed)
            {
                SetupProvider.Singleton.SetMusic(actualGameType);
                SetupProvider.Singleton.LoadMap(actualGameType);
                SetupProvider.Singleton.SetUI(actualGameType);
                SetupProvider.Singleton.CreateGameWindow(actualGameType);
                changed = false;
            }

            EngineConsole.Instance.OnTick(delta);
            ScreenControlManager.Instance.DoTick(delta);
        }

        protected override void OnRenderFrame()
        {
            base.OnRenderFrame();
            ScreenControlManager.Instance.DoRender();
        }

        protected override void OnRenderScreenUI(GuiRenderer renderer)
        {
            base.OnRenderScreenUI(renderer);
            if (Map.Instance != null)
                Map.Instance.DoDebugRenderUI(renderer);
            ScreenControlManager.Instance.DoRenderUI(renderer);
            EngineConsole.Instance.OnRenderUI(renderer);
        }
    }
}
